/*
 * Proc Example
 *
 * Desc: Create a proc file and provides read and write operation
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>

#include <linux/proc_fs.h>
#include <linux/uaccess.h>

#define MY_PROC  "myproc"
#define DEFAULT_LIMIT 50


struct proc_dir_entry *my_proc;

static unsigned long limit;

static ssize_t my_proc_read(struct file *filp, char __user *buf,
	size_t size, loff_t *ppos)
{
	char kbuf[16] = {0};
	size_t ret;

	if (size < sizeof(limit)) {
		pr_warn("%s: size=%ld beyond limit=%lu\n", __func__, size, sizeof(limit));
		return -EINVAL;
	}

	//consider cat special behavior
	if (*ppos > 0) {
		ret = 0;
		goto exit;
	}

	snprintf(kbuf, sizeof(buf), "%lu\n", limit);

	ret = simple_read_from_buffer(buf, size, ppos, kbuf, strlen(kbuf));
	if (ret < 0)
		pr_err("%s: simple_read_from_buffer failed, ret=%ld\n", __func__, ret);
	else
		*ppos += ret;

exit:
	return ret;
}

static ssize_t my_proc_write(struct file *filp, const char __user *buf,
	size_t size, loff_t *ppos)
{
	char kbuf[16];

	pr_info("%s...\n", __func__);

	if (size > sizeof(kbuf) - 1) {
		pr_warn("%s: size=%ld beyond limit=%lu\n", __func__, size, sizeof(kbuf)-1);
		return -EINVAL;
	}

	if (copy_from_user(kbuf, buf, size)) {
		pr_err("%s: failed to copy_from_user\n", __func__);
		return -EFAULT;
	}

	limit = simple_strtoul(kbuf, NULL, 10);  //kstrol

	return size;
}



static const struct file_operations my_proc_fops = {
	.read = my_proc_read,
	.write = my_proc_write,
};

static int __init mydrv_init(void)
{
	pr_info("%s...\n", __func__);

	limit = DEFAULT_LIMIT;

	my_proc = proc_create(MY_PROC, 0664, NULL, &my_proc_fops);
	if (!my_proc) {
		pr_err("proc_create: %s failed\n", MY_PROC);
		return -ENOMEM;
	}


	return 0;
}

static void __exit mydrv_exit(void)
{
	pr_info("%s...\n", __func__);

	if (my_proc)
		proc_remove(my_proc);
}

module_init(mydrv_init);
module_exit(mydrv_exit);

MODULE_AUTHOR("mydrv");
MODULE_LICENSE("GPL");
